home *** CD-ROM | disk | FTP | other *** search
- /*
- * main.c
- *
- * main routine for the scanner drivers, scanner callback
- * functions.
- *
- * This file should NOT be modified to support new scanners!!
- *
- * Copyright 1992, 1993, 1994 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
-
- #include <sys/types.h>
- #include <sys/prctl.h>
- #include <sys/wait.h>
-
- #include <unistd.h>
- #include <stdio.h>
- #include <signal.h>
- #include <errno.h>
- #include <dslib.h>
- #include <malloc.h>
- #include <stdlib.h>
- #include <invent.h>
- #include <bstring.h>
- #include <ulocks.h>
- #include <string.h>
- #include <locale.h>
-
- #include <msgs/uxsgiimpr.h>
-
- #include <scanner.h>
- #include <scanipc.h>
- #include <scandrv.h>
- #include <scanconv.h>
-
- #include "scan.h"
-
- /*
- * Since the units of resolution are in the denominator, going from
- * inches to centimeters and back is the inverse of what it is when
- * the units are in the numerator.
- */
- #define RESINCHTOCM(inch) CMTOINCH(inch)
- #define RESCMTOINCH(cm) INCHTOCM(cm)
-
- #define ISRGBPIX8(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 3 && \
- (t)->type == SC_RGB && \
- (t)->bpp == 8)
-
- #define ISRGBPIX16(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 3 && \
- (t)->type == SC_RGB && \
- (t)->bpp > 8)
-
- #define ISRGBPIX1(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 3 && \
- (t)->type == SC_RGB && \
- (t)->bpp == 1)
-
- #define ISRGBPLANE8(t) ((t)->packing == SC_PACKPLANE && \
- (t)->channels == 3 && \
- (t)->type == SC_RGB && \
- (t)->bpp == 8)
-
- #define ISRGBPLANE16(t) ((t)->packing == SC_PACKPLANE && \
- (t)->channels == 3 && \
- (t)->type == SC_RGB && \
- (t)->bpp > 8)
-
- #define ISGREY8(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 1 && \
- (t)->type == SC_GREY && \
- (t)->bpp == 8)
-
- #define ISGREY16(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 1 && \
- (t)->type == SC_GREY && \
- (t)->bpp > 8)
-
- #define ISMONO1(t) ((t)->packing == SC_PACKPIX && \
- (t)->channels == 1 && \
- (t)->type == SC_MONO && \
- (t)->bpp == 1)
-
-
- int drverr;
- char *drvmsg;
-
- extern int mpin(void *addr, unsigned len);
- extern int munpin(void *addr, unsigned len);
-
- static pid_t scanpid, imgpid; /* child process ids */
- static SCANINFO *scan; /* info about the scanner */
- static SCANPARAMS sparams; /* info about the scan to take place */
- static char *scanbuf; /* memory to buffer scan lines */
- static unsigned bytesPinned = 0; /* Number of bytes pinned into */
- /* memory */
- static long maxmem; /* max memory to allocat for */
- /* scanbuf */
- static long xpixels, xbytes, ysize; /* The size of the scan data we */
- /* will be returning to the */
- /* scanning application. The */
- /* fields of sparams describes the */
- /* size of the scan data that the */
- /* scan module will be returning */
- /* to us */
- static char *ibuf = NULL; /* Image processing line buffer */
- static int *zmap = NULL; /* Zoom map */
- static SCSTATUS status; /* Current scan status */
- static int kids = 0; /* Number of child processes */
- /* running */
-
- /*
- * Lin conversion function for DoImgProc
- */
- static void (*convert)(void *from, int fx, void *to, int tx, int *z);
-
- /*
- * static void
- * ScanCleanup(void)
- *
- * Description:
- * Free all the resources used by a scan, preparing things for a
- * subsequent scanning operation.
- *
- * This should not be called from a signal handler (specifically,
- * childdeath); use SCDriverSyncFunction. It should never be
- * called if either of the child processes are still around.
- */
-
- static void
- ScanCleanup(void)
- {
- int statusPut = 0;
- /*
- * Get rid of the queues and free memory
- */
- if (sparams.scanq) {
- SCDestroyQueue(sparams.scanq);
- sparams.scanq = NULL;
- }
-
- if (sparams.sfreeq) {
- SCDestroyQueue(sparams.sfreeq);
- sparams.sfreeq = NULL;
- }
-
- if (scanbuf) {
- if (bytesPinned) {
- if (munpin(scanbuf, bytesPinned) < 0) {
- #ifdef DEBUG
- perror("munpin");
- #endif
- }
- bytesPinned = 0;
- }
- free(scanbuf);
- scanbuf = NULL;
- }
-
- if (ibuf) {
- free(ibuf);
- ibuf = NULL;
- }
-
- if (zmap) {
- SCDestroyZoomMap(zmap);
- zmap = NULL;
- }
-
- if (status.state == SC_ERROR) {
- SCDriverPutStatus(&status,
- status.errno == SCEDRVMSG ? drvmsg : NULL);
- statusPut = 1;
- }
-
- if (status.state != SC_READY) {
- status.state = SC_READY;
- status.curline = 0;
- status.pass = 0;
- if (!statusPut) {
- SCDriverPutStatus(&status, NULL);
- }
- (void)ScanReset(scan);
- }
- }
-
- /*
- * static void
- * EndScanning(void)
- *
- * Description:
- * Tell the child processes that are doing the scanning and
- * converting to exit. SCQueueSetExit causes processes that call
- * SCEnqueue or SCDequeue on that queue to exit, and
- * SCDriverStopWriter causes processes that call SCDriverPutRow
- * to exit.
- */
-
- static void
- EndScanning(void)
- {
- if (sparams.scanq) {
- SCQueueSetExit(sparams.scanq);
- }
-
- if (sparams.sfreeq) {
- SCQueueSetExit(sparams.sfreeq);
- }
-
- SCDriverStopWriter(imgpid);
- }
-
- /*
- * static void
- * childdeath(int sig)
- *
- * Description:
- * Either scanproc or imgproc exited. wait() to find the exit
- * status; if exit status was non-zero, then drverr contains a
- * suitable error code and we set the status indicate that an
- * error occurred. If the process died due to a signal, we don't
- * really know what happened so we'll call it a device error.
- *
- * Parameters:
- * sig - signal that caused us to be called (SIGCLD)
- */
-
- /*ARGSUSED*/
- static void
- childdeath(int sig)
- {
- pid_t pid;
- int stat;
-
- for (pid = waitpid(-1, &stat, WNOHANG);
- pid > 0;
- pid = waitpid(-1, &stat, WNOHANG)) {
- kids--;
- if (status.state == SC_SCANNING &&
- (WIFEXITED(stat) && WEXITSTATUS(stat) != 0
- || WIFSIGNALED(stat))) {
- if (WIFSIGNALED(stat)) {
- drverr = SCEDEV;
- }
- /*
- * Don't actually call SCDriverPutStatus here, because
- * that can lead to deadlock. ScanCleanup should notice
- * that an error has occurred and set status
- * appropriately (but it doesn't right now).
- */
- status.state = SC_ERROR;
- status.errno = drverr;
- /*
- * Call EndScanning synchronously. This will have the
- * effect of ending the other child process. When the
- * other child process has exited, we'll end up in this
- * signal handler again, kids will be 0, and we'll cause
- * ScanCleanup to be called.
- */
- SCDriverSyncFunction((void (*)(void *))EndScanning, NULL);
- }
- /*
- * Once they're both dead, do the cleanup
- */
- if (kids == 0) {
- SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
- }
- }
- }
-
- /*
- * void
- * ExitCleanup(void)
- *
- * Description:
- * Abort a scan if one is in progress, since we're about to exit.
- * We can't just call AbortScan, because it relies on processing
- * of SIGCHLD and SCDriverSyncFunction to call ScanCleanup.
- * We'll never end up back in SCDriverMainLoop() after this, so
- * we have to take care of everything inline.
- */
-
- void
- ExitCleanup(void)
- {
- sigset_t mask, omask;
- int stat;
-
- if (status.state == SC_SCANNING) {
- (void)sigemptyset(&mask);
- (void)sigaddset(&mask, SIGCHLD);
- (void)sigprocmask(SIG_BLOCK, &mask, &omask);
-
- EndScanning();
-
- (void)waitpid(scanpid, &stat, 0);
- (void)waitpid(imgpid, &stat, 0);
-
- (void)ScanCleanup();
-
- (void)sigprocmask(SIG_SETMASK, &omask, 0);
- }
- }
-
- /*
- * static void
- * hangup(int sig)
- *
- * Description:
- * SIGHUP handler. If our parent has exited, we should exit too.
- *
- * Parameters:
- * sig SIGHUP
- */
-
- /*ARGSUSED*/
- static void
- hangup(int sig)
- {
- if (getppid() == 1) {
- ExitCleanup();
- exit(0);
- }
- }
-
- /*
- * static void
- * SetMaxMem(void)
- *
- * Description:
- * Set maxmem, the upper limit on how much memory we'll malloc to
- * do scanning.
- */
-
- static void
- SetMaxMem(void)
- {
- inventory_t *inv;
-
- if (setinvent() == 0) {
- while ((inv = getinvent()) != NULL) {
- if (inv->inv_class == INV_MEMORY && inv->inv_type == INV_MAIN) {
- /*
- * Don't use more than 1/3 of main memory
- */
- maxmem = inv->inv_state / 3;
- endinvent();
- return;
- }
- }
- endinvent();
- }
- /*
- * Default to 2 meg
- */
- maxmem = 2 * 1024 * 1024;
- }
-
- /*
- * void
- * scanfunc(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * What follows are many similar functions that implement the
- * server side of the scanning protocol. These commands are
- * called from within SCDriverMainLoop(), which knows which
- * function to call for a given command because we passed in
- * scanTable. The order of the functions in scanTable is very
- * important; the index in the table of each function must be the
- * same as its SCN_ #define in <scanipc.h>.
- *
- * Each function gets called with its SCN_ #define as the first
- * argument, the argument data as the second argument, and a
- * result structure as the third argument. In general, each
- * function casts the argument data (arg->data) to the
- * appropriate type, and executes appropriately, making res->data
- * point to a buffer containing the results.
- *
- * In the event of an error, the function should set res->errno
- * to a value either from <errno.h> or from <scanner.h> and
- * return. The library will report an error to the application
- * if res->errno is non-zero.
- *
- * The library initializes all fields of the SCRES structure
- * pointed to by res to 0 before calling each function.
- *
- * Parameters:
- * cmd SCN_ #define for the command
- * arg argument structure
- * res results structure
- */
-
- /*
- * static void
- * InitOK(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Make sure driver initialized ok
- */
-
- /*ARGSUSED*/
- static void
- InitOK(int cmd, SCARG *arg, SCRES *res)
- {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- res->len = 0;
- res->free = 0;
- }
-
- /*
- * static void
- * Die(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Obey the scanner application's order to die
- */
-
- /*ARGSUSED*/
- static void
- Die(int cmd, SCARG *arg, SCRES *res)
- {
- ExitCleanup();
- exit(0);
- }
-
- /*
- * static void
- * ResMinMax(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Inform the scanner application of the minimum and maximum
- * values for the horizontal and vertical resolutions. Convert
- * between metrics as necessary.
- *
- * Parameters:
- * cmd SCN_MINMAXRES
- * arg metric
- * res SCMINMAXRES
- */
-
- /*ARGSUSED*/
- static void
- ResMinMax(int cmd, SCARG *arg, SCRES *res)
- {
- static SCMINMAXRES r;
- /*
- * Test arg->data first so as not to break compatibility with
- * applications built with old buggy library.
- */
- int metric = arg->data ? *(int *)arg->data : SC_INCHES;
-
- if (metric == scan->metric) {
- r.maxx = scan->maxxres;
- r.maxy = scan->maxyres;
- } else if (metric == SC_INCHES) {
- r.maxx = RESCMTOINCH(scan->maxxres);
- r.maxy = RESCMTOINCH(scan->maxyres);
- } else if (metric == SC_CENTIM) {
- r.maxx = RESINCHTOCM(scan->maxxres);
- r.maxy = RESINCHTOCM(scan->maxyres);
- } else {
- res->errno = SCEBADMETRIC;
- return;
- }
-
- /*
- * Always set the min res to 1; We'll zoom down from some hardware
- * supported resolution to this.
- */
- r.minx = 1;
- r.miny = 1;
- res->data = &r;
- res->len = sizeof(r);
- }
-
- /*
- * static void
- * NumRes(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Inform the scanner application of the number of hardware
- * resolutions supported. If this number is 0, we're telling the
- * application that ALL resolutions are supported in hardware, or
- * that the scanner itself is capable of doing very high quality
- * rescaling of the image data as it scans it.
- *
- * Parameters:
- * cmd SCN_NRES
- * arg void
- * res pointer to int
- */
-
- /*ARGSUSED*/
- static void
- NumRes(int cmd, SCARG *arg, SCRES *res)
- {
- res->data = &scan->nres;
- res->len = sizeof(scan->nres);
- }
-
- /*
- * static void
- * HardwareRes(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Inform scanner application of the scanning resolutions
- * supported by the scanner itself, without having us do
- * decimation or replication. Convert metrics as appropriate.
- *
- * Parameters:
- * cmd SCN_RES
- * arg metric
- * res array of float
- */
-
- /*ARGSUSED*/
- static void
- HardwareRes(int cmd, SCARG *arg, SCRES *res)
- {
- static float *r;
- int metric = *(int *)arg->data;
- int i;
-
- if (scan->nres) {
- if (r) {
- free(r);
- r = 0;
- }
- r = calloc(scan->nres * 2, sizeof(*r));
-
- for (i = 0; i < scan->nres; i++) {
- if (metric == scan->metric) {
- r[i] = scan->xres[i];
- r[i + scan->nres] = scan->yres[i];
- } else if (metric == SC_INCHES) {
- r[i] = RESCMTOINCH(scan->xres[i]);
- r[i + scan->nres] = RESCMTOINCH(scan->yres[i]);
- } else if (metric == SC_CENTIM) {
- r[i] = RESINCHTOCM(scan->xres[i]);
- r[i + scan->nres] = RESINCHTOCM(scan->yres[i]);
- } else {
- res->errno = SCEBADMETRIC;
- return;
- }
- }
-
- }
-
- res->len = sizeof(*r) * scan->nres * 2;
- res->data = r;
- }
-
- /*
- * static void
- * NumTypes(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Return to scanner application the number of types supported by
- * this driver/scanner.
- *
- * Parameters:
- * cmd SCN_NTYPES
- * arg void
- * res int
- */
-
- /*ARGSUSED*/
- static void
- NumTypes(int cmd, SCARG *arg, SCRES *res)
- {
- res->data = &scan->ntypes;
- res->len = sizeof(scan->ntypes);
- return;
- }
-
- /*
- * static void
- * Types(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Tell scanner application what types we support
- *
- * Parameters:
- * cmd SCN_TYPES
- * arg void
- * res array of SCDATATYPE
- */
-
- /*ARGSUSED*/
- static void
- Types(int cmd, SCARG *arg, SCRES *res)
- {
- res->data = scan->types;
- res->len = scan->ntypes * sizeof(SCDATATYPE);
- }
-
- /*
- * static void
- * AbortScan(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Stop scanning. Induce the child processes to exit. Arrange
- * to have ScanCleanup called.
- *
- * Parameters:
- * cmd SCN_ABORT
- * arg void
- * res void
- */
-
- /*ARGSUSED*/
- static void
- AbortScan(int cmd, SCARG *arg, SCRES *res)
- {
- sigset_t mask, omask;
- int stat;
-
- if (status.state != SC_SCANNING) {
- res->errno = SCENOTSCANNING;
- return;
- }
-
- /*
- * Block SIGCHLD while we're waiting for the children to exit, so
- * that SIGCHLD handler won't wait instead.
- */
- (void)sigemptyset(&mask);
- (void)sigaddset(&mask, SIGCHLD);
- (void)sigprocmask(SIG_BLOCK, &mask, &omask);
-
- EndScanning();
-
- (void)waitpid(scanpid, &stat, 0);
- (void)waitpid(imgpid, &stat, 0);
- kids = 0;
-
- /*
- * Restore the old signal mask.
- */
- (void)sigprocmask(SIG_SETMASK, &omask, NULL);
- /*
- * Call SCDriverSyncFunction instead of ScanCleanup so that the
- * app doesn't have to wait for us to do this unless it tries to
- * issue another command
- */
- SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
- }
-
- /*
- * Forward declaration for StartScan
- */
- static void
- DoImgProc(SCANPARAMS *params);
-
- /*
- * static void
- * StartScan(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Start scanning. Allocate scan buffers and queues, sproc the
- * children to do the actual work.
- *
- * Parameters:
- * cmd SCN_SCAN
- * arg void
- * res void
- */
-
- static void
- StartScan(int cmd, SCARG *arg, SCRES *res)
- {
- long scanlinebytes, qlines;
- char *buf;
- sigset_t mask, omask;
-
- if (status.state != SC_READY) {
- res->errno = status.state == SC_SCANNING ? SCEBUSY : drverr;
- if (res->errno == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- return;
- }
-
- /*
- * Allocate memory for the scan queue
- *
- * Make sure that each line is aligned on a 4 byte boundary. This
- * is (so far) the lowest common denominator for allowing this
- * code to work for all encountered scanners; scsi scanners
- * require that buffers passed to the read routine are aligned on
- * 4 byte boundaries, and the screen driver needs this as well for
- * calling readdisplay().
- */
- scanlinebytes = (sparams.xbytes + 3) & ~0x3;
-
- if (!sparams.readlines) {
- sparams.readlines =
- MIN(sparams.maxmem / (scanlinebytes * 2), sparams.ylines);
-
- if (!sparams.readlines) {
- sparams.readlines = 1;
- }
- }
-
- /*
- * If readlines > ylines, no buffers will be put on the freeq in
- * the code below, which will prevent any scanning at all from
- * occurring.
- */
- if (sparams.readlines > sparams.ylines) {
- sparams.readlines = sparams.ylines;
- }
-
- qlines = MIN(sparams.readlines * 2, sparams.ylines);
-
- bytesPinned = qlines * scanlinebytes;
- scanbuf = malloc(bytesPinned);
-
- if (mpin(scanbuf, bytesPinned) < 0) {
- #ifdef DEBUG
- perror("mpin");
- #endif
- bytesPinned = 0;
- }
-
- sparams.scanq = SCCreateQueue(qlines);
- sparams.sfreeq = SCCreateQueue(qlines / sparams.readlines);
-
- /*
- * Chop the chunk we malloc'd into scanlinebyte-sized chunk *
- * readlines sized chunks.
- */
- buf = scanbuf;
- for (buf = scanbuf;
- qlines >= sparams.readlines;
- qlines -= sparams.readlines) {
- SCEnqueue(sparams.sfreeq, buf);
- buf += scanlinebytes * sparams.readlines;
- }
-
- /*
- * Set things up for DoImgProc
- *
- * SetupScan is supposed to set sparams.convert if any conversion
- * is necessary to achieve one of the four basic types. The
- * standard conversion routines all do zooming if necessary, so we
- * don't have to worry about choosing an appropriate zoom function
- * if we're already converting.
- */
- convert = sparams.convert;
- if (xpixels != sparams.xpixels) {
- if (!convert) {
- switch (sparams.type.type) {
- case SC_RGB:
- convert = sparams.type.packing == SC_PACKPIX ?
- SCZoomRow24 : SCZoomRow8;
- break;
- case SC_GREY:
- convert = SCZoomRow8;
- break;
- case SC_MONO:
- convert = SCZoomRow1;
- break;
- default:
- drverr = SCEBADTYPE;
- exit(1);
- }
- }
- zmap = SCCreateZoomMap(sparams.xpixels, xpixels);
- }
-
- if (convert) {
- ibuf = malloc(xbytes);
- }
-
- /*
- * Set status BEFORE creating children. If we call
- * SCDriverPutStatus when we have children, deadlock can occur.
- */
- status.state = SC_SCANNING;
- status.curline = 0;
- SCDriverPutStatus(&status, NULL);
-
- /*
- * We must block SIGCHLD while we're creating child processes,
- * because our SIGCHLD signal handler uses the variable kids to
- * determine whether or not scanning is going on. It's possible
- * that one of our sproc children could exit before we got to the
- * code that incremented kids, so that the count seen by the
- * signal handler would be wrong.
- *
- * Yes, this actually happened.
- */
- (void)sigemptyset(&mask);
- (void)sigaddset(&mask, SIGCHLD);
- (void)sigprocmask(SIG_BLOCK, &mask, &omask);
-
- /*
- * Set up child processes to do the scanning and zooming. DoScan
- * gets buffers from sfreeq, scans data into them, and then puts
- * them on scanq. DoImgProc takes buffers from scanq, zooms and
- * converts them, and dispatches them with SCDriverPutRow.
- */
- imgpid = sproc((void (*)(void *))DoImgProc, PR_SADDR,
- (unsigned)&sparams);
- if (imgpid < 0) {
- (void)sigprocmask(SIG_SETMASK, &omask, 0);
- AbortScan(cmd, arg, res);
- res->errno = errno;
- return;
- }
- kids++;
-
- scanpid = sproc((void (*)(void *))DoScan, PR_SADDR,
- (unsigned)&sparams);
-
- if (scanpid < 0) {
- (void)sigprocmask(SIG_SETMASK, &omask, 0);
- AbortScan(cmd, arg, res);
- res->errno = errno;
- return;
- }
- kids++;
- (void)sigprocmask(SIG_SETMASK, &omask, 0);
- }
-
- /*
- * static void
- * Setup(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Set up the scanner with the scan window and resolution in
- * anticipation of a pending scan. Perform all necessary metric
- * conversions.
- *
- * Parameters:
- * cmd SCN_SETUP
- * arg SCSETUP
- * res void
- */
-
- /*ARGSUSED*/
- static void
- Setup(int cmd, SCARG *arg, SCRES *res)
- {
- SCSETUP *s;
- float xres, yres;
- int i;
-
- s = arg->data;
-
- bzero(&sparams, sizeof sparams);
- sparams.s = scan;
- sparams.preview = s->preview;
- if (scan->metric != s->rmetric) {
- if (scan->metric == SC_INCHES) {
- xres = RESCMTOINCH(s->xres);
- yres = RESCMTOINCH(s->yres);
- } else {
- xres = RESINCHTOCM(s->xres);
- yres = RESINCHTOCM(s->yres);
- }
- } else {
- xres = s->xres;
- yres = s->yres;
- }
-
- /*
- * Set the sparams.xres and sparams.yres to the next highest
- * resolution that the scanner actually supports, or the highest
- * resolution if the scanner doesn't support resolutions higher
- * than those requested.
- */
- if (scan->nres && !scan->canZoom) {
- for (i = 0; i < scan->nres; i++) {
- if (scan->xres[i] >= xres || i == scan->nres - 1) {
- sparams.xres = scan->xres[i];
- break;
- }
- }
-
- for (i = 0; i < scan->nres; i++) {
- if (scan->yres[i] >= yres || i == scan->nres -1) {
- sparams.yres = scan->yres[i];
- break;
- }
- }
- } else {
- sparams.xres = ilimit(xres, scan->minxres, scan->maxxres);
- sparams.yres = ilimit(yres, scan->minyres, scan->maxyres);
- }
-
- switch (s->wmetric) {
- case SC_PIXELS:
- sparams.x = s->x / xres;
- sparams.y = s->y / yres;
- sparams.width = s->width / xres;
- sparams.height = s->height / yres;
- xpixels = s->width;
- ysize = s->height;
- break;
- case SC_INCHES:
- if (scan->metric == SC_INCHES) {
- sparams.x = s->x;
- sparams.y = s->y;
- sparams.width = s->width;
- sparams.height = s->height;
- } else {
- sparams.x = INCHTOCM(s->x);
- sparams.y = INCHTOCM(s->y);
- sparams.width = INCHTOCM(s->width);
- sparams.height = INCHTOCM(s->height);
- }
- xpixels = s->width * (s->rmetric == SC_INCHES ? s->xres :
- RESCMTOINCH(s->xres));
- ysize = s->height * (s->rmetric == SC_INCHES ? yres :
- RESCMTOINCH(s->yres));
- break;
- case SC_CENTIM:
- if (scan->metric == SC_CENTIM) {
- sparams.x = s->x;
- sparams.y = s->y;
- sparams.width = s->width;
- sparams.height = s->height;
- } else {
- sparams.x = CMTOINCH(s->x);
- sparams.y = CMTOINCH(s->y);
- sparams.width = CMTOINCH(s->width);
- sparams.height = CMTOINCH(s->height);
- }
- xpixels = s->width * (s->rmetric == SC_CENTIM ? s->xres :
- RESINCHTOCM(s->xres));
- ysize = s->height * (s->rmetric == SC_CENTIM ? yres :
- RESINCHTOCM(s->yres));
- break;
- default:
- res->errno = SCENOTSUPPORTED;
- return;
- }
-
- sparams.type = s->type;
-
- sparams.maxmem = maxmem;
- if (SetupScan(&sparams) < 0) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- return;
- }
-
- /*
- * Don't do any zooming if we don't have to. Our calculations
- * above may have yielded slightly different results than similar
- * calculations in the scanner, and unless we were asked to return
- * a specific number of pixels, we figure that image quality is
- * the over-riding concern.
- */
- if (xres == sparams.xres && yres == sparams.yres && s->wmetric !=
- SC_PIXELS) {
- xpixels = sparams.xpixels;
- ysize = sparams.ylines;
- }
-
- /*
- * Return an error if this scan would be a no-op.
- */
- if (sparams.xpixels == 0 || sparams.ylines == 0 || xpixels == 0 ||
- ysize == 0) {
- res->errno = SCEAREATOOSMALL;
- return;
- }
-
- if (ISRGBPIX8(&s->type)) {
- xbytes = xpixels * 3;
- } else if (ISRGBPIX16(&s->type)) {
- xbytes = xpixels * 6;
- } else if (ISRGBPIX1(&s->type)) {
- xbytes = (xpixels + 1) / 2;
- } else if (ISRGBPLANE8(&s->type) || ISGREY8(&s->type)) {
- xbytes = xpixels;
- } else if (ISRGBPLANE16(&s->type) || ISGREY16(&s->type)) {
- xbytes = xpixels * 2;
- } else if (ISMONO1(&s->type)) {
- xbytes = (xpixels + 7) / 8;
- } else {
- res->errno = SCEBADTYPE;
- return;
- }
- }
-
- /*
- * static void
- * GetSize(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Return to the scanning application the size of the current
- * scan, that set by the last call to Setup.
- *
- * Parameters:
- * cmd SCN_GETSIZE
- * arg void
- * res SCSIZE
- */
-
- /*ARGSUSED*/
- static void
- GetSize(int cmd, SCARG *arg, SCRES *res)
- {
- static SCSIZE sz;
-
- sz.xbytes = xbytes;
- sz.xpixels = xpixels;
- sz.ysize = ysize;
- res->data = &sz;
- res->len = sizeof sz;
- }
-
- /*
- * static void
- * PageSize(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Inform the scanning application of the (x,y,width,height)
- * coordinates of the supported scanning area, converting between
- * metrics as necessary
- *
- * Parameters:
- * cmd SCN_PAGESIZE
- * arg metric
- * res SCWINDOW
- */
-
- /*ARGSUSED*/
- static void
- PageSize(int cmd, SCARG *arg, SCRES *res)
- {
- static SCWINDOW w = { 0, 0, 0, 0};
- int metric = *(int *)arg->data;
-
- if (metric != SC_INCHES && metric != SC_CENTIM) {
- res->errno = SCEBADMETRIC;
- return;
- }
-
- if (metric == scan->metric) {
- w.x = scan->pagex;
- w.y = scan->pagey;
- w.width = scan->pagewidth;
- w.height = scan->pageheight;
- } else {
- if (metric == SC_INCHES) {
- w.x = CMTOINCH(scan->pagex);
- w.y = CMTOINCH(scan->pagey);
- w.width = CMTOINCH(scan->pagewidth);
- w.height = CMTOINCH(scan->pageheight);
- } else {
- w.x = INCHTOCM(scan->pagex);
- w.y = INCHTOCM(scan->pagey);
- w.width = INCHTOCM(scan->pagewidth);
- w.height = INCHTOCM(scan->pageheight);
- }
- }
-
- res->data = &w;
- res->len = sizeof w;
- }
-
- /*
- * void
- * FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Get this scanner's feeder flags for the scanner application
- *
- * Parameters:
- * cmd SCN_FEEDERGETFLAGS
- * arg none
- * res SCFEEDERFLAGS *
- */
-
- /*ARGSUSED*/
- void
- FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
- {
- res->data = &scan->feederFlags;
- res->len = sizeof scan->feederFlags;
- }
-
- /*
- * void
- * FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Set the feeder flags passed by the scanner application. This
- * is used to select automatic or programmatic feeding for
- * scanners that support both.
- *
- * Parameters:
- * cmd SCN_FEEDERSETFLAGS
- * arg SCFEEDERFLAGS *
- * res none
- */
-
- /*ARGSUSED*/
- void
- FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
- {
- if (!(scan->feederFlags & SC_HASFEEDER)) {
- res->errno = SCENOFEEDER;
- return;
- }
- if (SetFeederFlags(scan, *(SCFEEDERFLAGS *)arg->data) < 0) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- }
- }
-
- /*
- * void
- * FeederAdvance(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Advance the feeder to the next document
- *
- * Parameters:
- * cmd SCN_FEEDERADVANCE
- * arg none
- * res none
- */
-
- /*ARGSUSED*/
- void
- FeederAdvance(int cmd, SCARG *arg, SCRES *res)
- {
- if (!(scan->feederFlags & SC_HASFEEDER)) {
- res->errno = SCENOFEEDER;
- return;
- }
- if (AdvanceFeeder(scan) < 0) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- }
- }
-
- /*
- * void
- * IsFeederReady(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Find out whether the document feeder is ready to be advanced;
- * that is, whether a call to FeederAdvance would be successful.
- *
- * Parameters:
- * cmd SCN_FEEDERREADY
- * arg none
- * res none
- */
-
- /*ARGSUSED*/
- void
- IsFeederReady(int cmd, SCARG *arg, SCRES *res)
- {
- if (!(scan->feederFlags & SC_HASFEEDER)) {
- res->errno = SCENOFEEDER;
- return;
- }
- if (FeederReady(scan) < 0) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- }
- }
-
- /*
- * void
- * GetVersion(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Tell the scanner app what version of libscan we linked with.
- *
- * Parameters:
- * cmd SCN_GETVERSION
- * arg none
- * res float *
- */
-
- /*ARGSUSED*/
- void
- GetVersion(int cmd, SCARG *arg, SCRES *res)
- {
- static float version = SC_VERSION;
-
- res->data = &version;
- res->len = sizeof version;
- }
-
- /*
- * void
- * GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Get options to save from the scanner. This function is used
- * for two different IPCs: SCN_GETSAVEOPTLEN and SCN_GETSAVEOPT.
- * We store the return value for SCN_GETSAVEOPT when
- * SCN_GETSAVEOPTLEN is called, so in case anything happens that
- * changes the size of the saveable options between these two
- * IPC's we don't end up writing inconsistent data back to the
- * app (although it will be stale).
- *
- * In practice these two commands should come one after the
- * other, so there shouldn't be any problem with storing the data
- * for a very short period of time.
- *
- * This also makes the interface with the scan.c portion of this
- * command very simple, which is very desirable.
- *
- * Parameters:
- * cmd SCN_GETSAVEOPTLEN or SCN_GETSAVEOPT
- * arg NULL
- * res int * or void *
- */
-
- void
- GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
- {
- static int nBytes;
- static void *optBuf = NULL, *options;
-
- if (cmd == SCN_GETSAVEOPTLEN) {
- options = GetSaveOptions(scan, &nBytes);
-
- if (!options) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- return;
- }
-
- if (optBuf) {
- free(optBuf);
- }
- optBuf = malloc(nBytes);
- bcopy(options, optBuf, nBytes);
- res->data = &nBytes;
- res->len = sizeof nBytes;
- } else {
- /*
- * This is actually a protocol error; the client libscan
- * should have called SCN_GETSAVEOPTLEN before calling
- * SCN_GETSAVEOPT
- */
- if (!options) {
- res->errno = SCENOSAVEOPT;
- return;
- }
- res->data = optBuf;
- res->len = nBytes;
- }
- }
-
- /*
- * void
- * SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
- *
- * Description:
- * Set options based on saved values from the scanner application
- *
- * Parameters:
- * cmd SCN_SETSAVEOPT
- * arg void *
- * res NULL
- */
-
- void
- SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
- {
- if (SetSaveOptions(scan, arg->data, arg->len) == -1) {
- res->errno = drverr;
- if (drverr == SCEDRVMSG) {
- res->errMsg = drvmsg;
- }
- }
- }
-
- /*
- * Table of functions to be passed to the scan library. Order is of
- * the utmost importance here; the position of each function in this
- * table corresponds to the SCN_ #define in <scanipc.h> of the
- * command that it implements.
- */
- SCANFUNC scanTable[] = {
- InitOK,
- Die,
- ResMinMax,
- NumRes,
- HardwareRes,
- NumTypes,
- Types,
- PageSize,
- Setup,
- StartScan,
- AbortScan,
- GetSize,
- FeederGetFlags,
- FeederSetFlags,
- FeederAdvance,
- IsFeederReady,
- GetVersion,
- /*
- * GetSaveOptionsCB is here twice because it's the callback for
- * two separate commands.
- */
- GetSaveOptionsCB,
- GetSaveOptionsCB,
- SetSaveOptionsCB
- };
-
- /*
- * static void
- * usage(char *pn)
- *
- * Description:
- * Print a usage line for a scanner driver
- *
- * Parameters:
- * pn program name to print usage for
- */
-
- static void
- usage(char *pn)
- {
- (void)fprintf(stderr, "usage: %s -query\n", pn);
- (void)fprintf(stderr, " %s -version\n", pn);
- (void)fprintf(stderr, " %s -install <device>\n", pn);
- (void)fprintf(stderr, " %s -delete <device>\n", pn);
- (void)fprintf(stderr, " %s <device> <arena>\n", pn);
- }
-
- /*
- * int
- * main(int argc, char *argv[])
- *
- * Description:
- * main routine for the scanner driver. Should be called
- * with argv[1] == name of the device which corresponds to the
- * scanner to open and argv[2] == name of the us arena file used
- * for communication with the scanning application.
- *
- * Parameters:
- * argc - number of command line arguments
- * argv - command line arguments
- *
- * Returns:
- * exit status
- */
-
- int
- main(int argc, char *argv[])
- {
- struct sigaction act;
-
- drverr = 0;
-
- (void)setlocale(LC_ALL, "");
-
- if (argc < 2) {
- usage(argv[0]);
- return 1;
- }
-
- if (strcmp(argv[1], "-query") == 0) {
- PrintID(stdout);
- FindScanners(stdout);
- exit(0);
- } else if (strcmp(argv[1], "-version") == 0) {
- /*
- * Don't change this. The install tool may use this
- * information in future versions of Impressario if the
- * communications protocol between clients and applications
- * change.
- */
- (void)printf("%g\n", SC_VERSION);
- exit(0);
- } else if (strcmp(argv[1], "-install") == 0) {
- if (argc != 3) {
- usage(argv[0]);
- return 1;
- }
- return InstallScanner(argv[2]) == 0 ? 0 : 1;
- } else if (strcmp(argv[1], "-delete") == 0) {
- if (argc != 3) {
- usage(argv[0]);
- return 1;
- }
- return DeleteScanner(argv[2]) == 0 ? 0 : 1;
- }
-
- if (argc != 3) {
- usage(argv[0]);
- return 1;
- }
-
- if (SCDriverInit(argv[2]) < 0) {
- drverr = errno;
- } else {
- scan = OpenScanner(argv[1]);
-
- SetMaxMem();
-
- act.sa_handler = childdeath;
- sigemptyset(&act.sa_mask);
- sigaddset(&act.sa_mask, SIGCHLD);
- act.sa_flags = 0;
-
- (void)sigaction(SIGCHLD, &act, NULL);
-
- status.state = SC_READY;
- status.curline = 0;
- status.pass = 0;
- SCDriverPutStatus(&status, NULL);
- }
-
- /*
- * Properly handle SIGHUP, which will get sent to us if our
- * parent, which is the scanning application, exits.
- */
- act.sa_handler = hangup;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- (void)sigaction(SIGHUP, &act, NULL);
- (void)sigaction(SIGQUIT, &act, NULL);
-
- /*
- * Call setsid, so we don't get signals propogated to us if our
- * parent dies. When that happens, we will get a SIGHUP, and
- * we'll clean up from there.
- */
- setsid();
-
- SCDriverSetCallbacks(scanTable, sizeof(scanTable)/sizeof(*scanTable),
- scan ? scan->options : NULL, scan ?
- scan->noptions : 0);
- SCDriverMainLoop();
- return 0;
- }
-
- /*
- * static void
- * DoImgProc(SCANPARAMS *params)
- *
- * Description:
- * Take the scanned data off of params->scanq, scale and convert
- * it to specification, and call SCDriverPutRow() with the
- * result.
- *
- * This is meant to be passed to sproc so that it runs as a child
- * thread of the main driver thread. It exits when it's done,
- * with a status of 0 if everything was OK and a status of 1 if
- * something went wrong. When exiting with a status of 1, we set
- * drverr to indicate what the problem was.
- *
- * WARNING:
- * Calls to SCEnqueue, SCDequeue, and SCDriverPutRow might not
- * return; ie these functions might call exit. If you modify
- * this code, be careful that you don't strand any resources due
- * to one of these three functions exiting.
- *
- * Parameters:
- * params parameters describing the scan
- */
-
- static void
- DoImgProc(SCANPARAMS *params)
- {
- int imgy, scany, cury;
- float fy;
- char *buf;
- char *tofree = 0;
- int npasses;
- int scanChunk;
-
- SCDriverInitChild();
- (void)prctl(PR_TERMCHILD);
-
- /*
- * scanChunk is the number of bytes in a chunk that sits on the
- * scanq. The scanning thread will typically scan many lines at a
- * time for efficiency, and break up the buffer into line
- * components for us to digest. It's then our job to put the
- * lines back together for the scanning thread.
- */
- scanChunk = ((params->xbytes + 3) & ~3) * params->readlines;
-
- cury = -1;
- imgy = 0;
- npasses = sparams.type.type == SC_RGB && sparams.type.packing ==
- SC_PACKPLANE ? 3 : 1;
-
- while (npasses--) {
- while (imgy < ysize) {
- /*
- * Zoom in the vertical direction; this involves either
- * repeating rows or skipping rows. We use GRIDTOFLOAT and
- * FLOATTOGRID to figure out which row we want this iteration,
- * and then skip rows until we get the right one.
- */
- fy = GRIDTOFLOAT(imgy, ysize);
- scany = FLOATTOGRID(fy, params->ylines);
- while(cury < scany) {
- cury++;
- if (cury % params->readlines == 0 && tofree) {
- SCEnqueue(params->sfreeq, tofree);
- tofree = 0;
- }
- buf = SCDequeue(params->scanq);
- if (((int)buf - (int)scanbuf) % scanChunk == 0) {
- /*
- * This should never happen. As long as tofree
- * occurs somewhere in each readlines lines, and
- * the tofree from one group of lines doesn't end
- * up in another group, we won't get here.
- */
- if (tofree) {
- SCEnqueue(params->sfreeq, tofree);
- tofree = 0;
- }
- tofree = buf;
- }
- }
-
- if (convert) {
- convert(buf, params->xpixels, ibuf, xpixels, zmap);
- }
-
- if (SCDriverPutRow(convert ? ibuf : buf, xbytes) < 0) {
- drverr = errno;
- exit(1);
- }
-
- imgy = imgy + 1 % ysize;
- status.curline = imgy;
- status.pass = imgy / ysize;
-
- SCDriverPutStatus(&status, NULL);
- }
-
- /*
- * Grab any lines at the end that we're skipping. We need to
- * do this so that the scanning thread doesn't get wedged
- * trying to enqueue scanned buffers, and also to clear things
- * out for multi-pass scanners.
- */
- while (cury++ < params->ylines - 1) {
- if (cury % params->readlines == 0 && tofree) {
- SCEnqueue(params->sfreeq, tofree);
- tofree = 0;
- }
- buf = SCDequeue(params->scanq);
- if (((int)buf - (int)scanbuf) % scanChunk == 0) {
- /*
- * This should never happen. As long as tofree
- * occurs somewhere in each readlines lines, and
- * the tofree from one group of lines doesn't end
- * up in another group, we won't get here.
- */
- if (tofree) {
- SCEnqueue(params->sfreeq, tofree);
- tofree = 0;
- }
- tofree = buf;
- }
- }
-
- cury = -1;
- imgy = 0;
- }
-
- /*
- * Not strictly necessary (because ScanCleanup destroys the queue
- * and frees the memory), but still good form.
- */
- if (tofree) {
- SCEnqueue(params->sfreeq, tofree);
- tofree = 0;
- }
- exit(0);
- }
-
-